package org.example.dbcompare.service;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.dbcompare.model.result.SqlExecutionItem;

import javax.sql.DataSource;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * SQL执行服务 - 获取详细执行信息
 * @author wuyong
 * @date 2025/01/22
 */
@Slf4j
public class SqlScriptExecutor {
    
    /**
     * 执行SQL并获取详细信息
     * @param dataSource 数据源
     * @param sql SQL语句
     * @return 执行结果
     */
    public SqlExecutionItem executeSqlWithDetails(DataSource dataSource, String sql) {
        LocalDateTime startTime = LocalDateTime.now();
        
        SqlExecutionItem.SqlExecutionItemBuilder resultBuilder = SqlExecutionItem.builder()
                .sql(sql)
                .startTime(startTime);
        
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            
            // 设置查询超时时间（30秒）
            statement.setQueryTimeout(30);
            
            // 执行SQL
            boolean hasResultSet = statement.execute(sql);
            
            // 获取影响行数
            int affectedRows = statement.getUpdateCount();
            
            // 处理结果集
            int resultSetRows = 0;
            if (hasResultSet) {
                try (ResultSet resultSet = statement.getResultSet()) {
                    resultSetRows = getResultSetRowCount(resultSet);
                }
            }
            
            // 获取警告信息
            List<String> warnings = getWarnings(statement);
            
            // 构建成功结果
            LocalDateTime endTime = LocalDateTime.now();
            resultBuilder.success(true)
                    .endTime(endTime)
                    .executionTimeMs(java.time.Duration.between(startTime, endTime).toMillis())
                    .affectedRows(affectedRows)
                    .hasResultSet(hasResultSet)
                    .resultSetRows(resultSetRows)
                    .warnings(warnings)
                    .executionDetails(buildExecutionDetails(hasResultSet, affectedRows, resultSetRows));
            
            log.info("SQL执行成功: {} - 影响行数: {}, 结果集行数: {}, 耗时: {}ms", 
                    sql.length() > 100 ? sql.substring(0, 100) + "..." : sql,
                    affectedRows, resultSetRows, resultBuilder.build().getExecutionTimeMs());
            
        } catch (SQLException e) {
            // 构建失败结果
            LocalDateTime endTime = LocalDateTime.now();
            resultBuilder.success(false)
                    .endTime(endTime)
                    .executionTimeMs(java.time.Duration.between(startTime, endTime).toMillis())
                    .errorMessage(e.getMessage())
                    .errorCode(e.getErrorCode())
                    .sqlState(e.getSQLState())
                    .executionDetails("SQL执行失败: " + e.getMessage());
            
            log.error("SQL执行失败: {} - 错误: {}, 错误代码: {}, SQL状态: {}", 
                    sql.length() > 100 ? sql.substring(0, 100) + "..." : sql,
                    e.getMessage(), e.getErrorCode(), e.getSQLState());
        }
        
        return resultBuilder.build();
    }
    
    /**
     * 批量执行SQL并获取详细信息
     * @param dataSource 数据源
     * @param sqlList SQL语句列表
     * @return 执行结果列表
     */
    public List<SqlExecutionItem> executeBatchSqlWithDetails(DataSource dataSource, List<String> sqlList) {
        List<SqlExecutionItem> results = new ArrayList<>();
        
        for (int i = 0; i < sqlList.size(); i++) {
            String sql = sqlList.get(i);
            log.info("执行第 {} 条SQL语句，共 {} 条", i + 1, sqlList.size());
            
            SqlExecutionItem result = executeSqlWithDetails(dataSource, sql);
            results.add(result);
            
            // 如果执行失败，可以选择是否继续执行后续SQL
            if (!result.isSuccess()) {
                log.warn("第 {} 条SQL执行失败，继续执行后续SQL", i + 1);
            }
        }
        
        return results;
    }
    
    /**
     * 执行SQL脚本文件并获取详细信息
     * @param dataSource 数据源
     * @param scriptContent 脚本内容
     * @return 执行结果列表
     */
    public List<SqlExecutionItem> executeScriptWithDetails(DataSource dataSource, String scriptContent) {
        // 解析SQL语句
        List<String> sqlStatements = parseSqlStatements(scriptContent);
        log.info("解析到 {} 条SQL语句", sqlStatements.size());
        
        return executeBatchSqlWithDetails(dataSource, sqlStatements);
    }
    
    /**
     * 获取结果集行数
     */
    private int getResultSetRowCount(ResultSet resultSet) throws SQLException {
        int rowCount = 0;
        while (resultSet.next()) {
            rowCount++;
        }
        return rowCount;
    }
    
    /**
     * 获取警告信息
     */
    private List<String> getWarnings(Statement statement) throws SQLException {
        List<String> warnings = new ArrayList<>();
        SQLWarning warning = statement.getWarnings();
        
        while (warning != null) {
            warnings.add(warning.getMessage());
            warning = warning.getNextWarning();
        }
        
        return warnings;
    }
    
    /**
     * 构建执行详情
     */
    private String buildExecutionDetails(boolean hasResultSet, int affectedRows, int resultSetRows) {
        if (hasResultSet) {
            return String.format("查询语句执行成功，返回 %d 行数据", resultSetRows);
        } else {
            return String.format("更新语句执行成功，影响 %d 行数据", affectedRows);
        }
    }
    
    /**
     * 解析SQL语句（简单实现）
     */
    private List<String> parseSqlStatements(String scriptContent) {
        List<String> statements = new ArrayList<>();
        
        if (StrUtil.isBlank(scriptContent)) {
            return statements;
        }
        
        // 按分号分割SQL语句
        String[] parts = scriptContent.split(";");
        
        for (String part : parts) {
            String trimmed = part.trim();
            if (StrUtil.isNotBlank(trimmed) && !trimmed.startsWith("--")) {
                statements.add(trimmed);
            }
        }
        
        return statements;
    }
    
    /**
     * 保持原有的简单执行方法（向后兼容）
     */
    public void executeSql(DataSource dataSource, String sql) {
        SqlExecutionItem result = executeSqlWithDetails(dataSource, sql);
        
        if (!result.isSuccess()) {
            throw new RuntimeException("执行SQL失败：" + sql + "，错误信息：" + result.getErrorMessage(), 
                    new SQLException(result.getErrorMessage(), result.getSqlState(), result.getErrorCode()));
        }
    }
}
